package cn.snmp.mutual.uk.stack;

import java.io.*;
import java.util.*;

import cn.snmp.mutual.uk.event.RequestPduListener;
import cn.snmp.mutual.uk.event.TrapListener;

public class SnmpContextPool implements SnmpContextFace {
    private static final String version_id = "@(#)$Id: SnmpContextPool.java,v 3.22 2009/03/05 13:27:41 birgita Exp $ Copyright Westhawk Ltd";
    protected static Hashtable contextPool;
    protected SnmpContext context;
    protected String hostname;
    protected String socketType;
    protected String bindAddr;
    protected int hostPort;
    protected String community;

    public SnmpContextPool(final String host, final int port) throws IOException {
        this(host, port, "public", null, "Standard");
    }

    public SnmpContextPool(final String host, final int port, final String typeSocket) throws IOException {
        this(host, port, "public", null, typeSocket);
    }

    public SnmpContextPool(final String host, final int port, final String comm, final String typeSocket) throws IOException {
        this(host, port, comm, null, typeSocket);
    }

    public SnmpContextPool(final String host, final int port, final String comm, final String bindAddress, final String typeSocket) throws IOException {
        this.context = null;
        this.community = "public";
        initPools();
        this.hostname = host;
        this.hostPort = port;
        this.community = comm;
        this.bindAddr = bindAddress;
        this.socketType = typeSocket;
        this.context = this.getMatchingContext();
    }

    private static synchronized void initPools() {
        if (SnmpContextPool.contextPool == null) {
            SnmpContextPool.contextPool = new Hashtable(5);
        }
    }

    public int getVersion() {
        return 0;
    }

    public String getHost() {
        return this.hostname;
    }

    public int getPort() {
        return this.hostPort;
    }

    public String getBindAddress() {
        return this.bindAddr;
    }

    public String getTypeSocket() {
        return this.socketType;
    }

    public String getSendToHostAddress() {
        String res = null;
        if (this.context != null) {
            res = this.context.getSendToHostAddress();
        }
        return res;
    }

    public String getReceivedFromHostAddress() {
        String res = null;
        if (this.context != null) {
            res = this.context.getReceivedFromHostAddress();
        }
        return res;
    }

    public String getCommunity() {
        return this.community;
    }

    public void setCommunity(final String newCommunity) {
        if (newCommunity != null && !newCommunity.equals(this.community)) {
            this.community = newCommunity;
            try {
                this.context = this.getMatchingContext();
            } catch (IOException ex) {
            }
        }
    }

    public boolean addPdu(final Pdu pdu) throws IOException, PduException {
        if (this.context == null) {
            this.context = this.getMatchingContext();
        }
        return this.context.addPdu(pdu);
    }

    public boolean removePdu(final int requestId) {
        boolean res = false;
        if (this.context != null) {
            res = this.context.removePdu(requestId);
        }
        return res;
    }

    public byte[] encodePacket(final byte msg_type, final int rId, final int errstat, final int errind, final Enumeration ve, final Object obj) throws IOException, EncodingException {
        byte[] res = null;
        if (this.context != null) {
            res = this.context.encodePacket(msg_type, rId, errstat, errind, ve, obj);
        }
        return res;
    }

    public void sendPacket(final byte[] packet) {
        if (this.context != null) {
            this.context.sendPacket(packet);
        }
    }

    public void destroy() {
        synchronized (SnmpContextPool.contextPool) {
            if (this.context != null) {
                final String hashKey = this.context.getHashKey();
                int count = 0;
                final SnmpContextPoolItem item = (SnmpContextPoolItem) SnmpContextPool.contextPool.get(hashKey);
                if (item != null) {
                    count = item.getCounter();
                    --count;
                    item.setCounter(count);
                }
                if (count <= 0) {
                    SnmpContextPool.contextPool.remove(hashKey);
                    this.context.destroy();
                }
                this.context = null;
            }
        }
    }

    public void destroyPool() {
        Hashtable copyOfPool = null;
        synchronized (SnmpContextPool.contextPool) {
            synchronized (SnmpContextPool.contextPool) {
                copyOfPool = (Hashtable) SnmpContextPool.contextPool.clone();
            }
            SnmpContextPool.contextPool.clear();
        }
        this.context = null;
        final Enumeration keys = copyOfPool.keys();
        while (keys.hasMoreElements()) {
            final String key = (String) keys.nextElement();
            final SnmpContextPoolItem item = (SnmpContextPoolItem) copyOfPool.get(key);
            if (item != null) {
                final SnmpContextBasisFace cntxt = item.getContext();
                cntxt.destroy();
            }
        }
        copyOfPool.clear();
    }

    public boolean isDestroyed() {
        boolean isDestroyed = true;
        if (this.context != null) {
            isDestroyed = this.context.isDestroyed();
        }
        return isDestroyed;
    }

    protected SnmpContext getMatchingContext() throws IOException {
        SnmpContextPoolItem item = null;
        SnmpContext newContext = null;
        final String hashKey = this.getHashKey();
        this.destroy();
        synchronized (SnmpContextPool.contextPool) {
            int count = 0;
            if (SnmpContextPool.contextPool.containsKey(hashKey)) {
                item = (SnmpContextPoolItem) SnmpContextPool.contextPool.get(hashKey);
                newContext = (SnmpContext) item.getContext();
                count = item.getCounter();
            } else {
                newContext = new SnmpContext(this.hostname, this.hostPort, this.bindAddr, this.socketType);
                newContext.setCommunity(this.community);
                item = new SnmpContextPoolItem(newContext);
                SnmpContextPool.contextPool.put(hashKey, item);
            }
            ++count;
            item.setCounter(count);
        }
        return newContext;
    }

    public void dumpContexts(final String title) {
        System.out.println(title + " " + SnmpContextPool.contextPool.size() + " context(s)");
        final Enumeration keys = SnmpContextPool.contextPool.keys();
        int i = 0;
        while (keys.hasMoreElements()) {
            final String key = (String) keys.nextElement();
            final SnmpContextPoolItem item = (SnmpContextPoolItem) SnmpContextPool.contextPool.get(key);
            if (item != null) {
                final int count = item.getCounter();
                final SnmpContext cntxt = (SnmpContext) item.getContext();
                if (cntxt == this.context) {
                    System.out.println("\tcurrent context: ");
                }
                System.out.println("\tcontext " + i + ": " + key + ", count: " + count + ", " + cntxt.toString() + "\n" + ", " + cntxt.getDebugString());
                ++i;
            }
        }
    }

    public String getHashKey() {
        final String str = this.hostname + "_" + this.hostPort + "_" + this.bindAddr + "_" + this.socketType + "_" + this.community + "_v" + this.getVersion();
        return str;
    }

    public void addTrapListener(final TrapListener l) throws IOException {
        if (this.context != null) {
            this.context.addTrapListener(l);
        }
    }

    public void removeTrapListener(final TrapListener l) throws IOException {
        if (this.context != null) {
            this.context.removeTrapListener(l);
        }
    }

    public void addTrapListener(final TrapListener l, final int port) throws IOException {
        if (this.context != null) {
            this.context.addTrapListener(l, port);
        }
    }

    public void removeTrapListener(final TrapListener l, final int port) throws IOException {
        if (this.context != null) {
            this.context.removeTrapListener(l, port);
        }
    }

    public void addTrapListener(final TrapListener l, final ListeningContextPool lcontext) throws IOException {
        if (this.context != null) {
            this.context.addTrapListener(l, lcontext);
        }
    }

    public void removeTrapListener(final TrapListener l, final ListeningContextPool lcontext) throws IOException {
        if (this.context != null) {
            this.context.removeTrapListener(l, lcontext);
        }
    }

    public void addRequestPduListener(final RequestPduListener l) throws IOException {
        if (this.context != null) {
            this.context.addRequestPduListener(l);
        }
    }

    public void removeRequestPduListener(final RequestPduListener l) throws IOException {
        if (this.context != null) {
            this.context.removeRequestPduListener(l);
        }
    }

    public void addRequestPduListener(final RequestPduListener l, final int port) throws IOException {
        if (this.context != null) {
            this.context.addRequestPduListener(l, port);
        }
    }

    public void removeRequestPduListener(final RequestPduListener l, final int port) throws IOException {
        if (this.context != null) {
            this.context.removeRequestPduListener(l, port);
        }
    }

    public void addRequestPduListener(final RequestPduListener l, final ListeningContextPool lcontext) throws IOException {
        if (this.context != null) {
            this.context.addRequestPduListener(l, lcontext);
        }
    }

    public void removeRequestPduListener(final RequestPduListener l, final ListeningContextPool lcontext) throws IOException {
        if (this.context != null) {
            this.context.removeRequestPduListener(l, lcontext);
        }
    }

    public Pdu processIncomingPdu(final byte[] message) throws DecodingException, IOException {
        Pdu pdu = null;
        if (this.context != null) {
            pdu = this.context.processIncomingPdu(message);
        }
        return pdu;
    }

    @Override
    public String toString() {
        String res = "";
        if (this.context != null) {
            res = this.context.toString();
        }
        return res;
    }

    public Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
}
